////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Michiel Helvensteijn                                    //
//                                                                            //
// This file is part of the Mist compiler.                                    //
//                                                                            //
// The Mist compiler is free software: you can redistribute it and/or modif   //
// it under the terms of the GNU General Public License as published by       //
// the Free Software Foundation, either version 3 of the License, or          //
// (at your option) any later version.                                        //
//                                                                            //
// The Mist compiler is distributed in the hope that it will be useful,       //
// but WITHOUT ANY WARRANTY; without even the implied warranty of             //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              //
// GNU General Public License for more details.                               //
//                                                                            //
// You should have received a copy of the GNU General Public License          //
// along with the Mist compiler.  If not, see <http://www.gnu.org/licenses/>. //
////////////////////////////////////////////////////////////////////////////////

#ifndef CODESTREAM_HPP
#define CODESTREAM_HPP

/******************************************************************************
 * Includes                                                                   *
 ******************************************************************************/

#include <ostream>
using std::ostream;
using std::endl;

#include <string>
using std::string;

class CodeStream;

/******************************************************************************
 * Stream Manipulators                                                        *
 ******************************************************************************/

/**
 * Add one level to the indentation depth of \a stream.
 * Use this as a manipulator with \a stream.operator<<.
 */
CodeStream& indent(CodeStream& stream);

/**
 * Reduce the indentation depth of \a stream by one level.
 * Use this as a manipulator with \a stream.operator<<.
 * 
 * @pre The current indentation depth of \a stream must be greater than 0.
 */
CodeStream& unindent(CodeStream& stream);

/******************************************************************************
 * CodeStream class                                                           *
 ******************************************************************************/

/**
 * An output stream class that can take any other output stream class and add
 * some extra functionality useful for printing code, like automatic
 * indentation. For the most part, it works just like ostream.
 * 
 * @see indent
 * @see unindent
 */
class CodeStream : public ostream {
	public:
		
		/**
		 * Construct a code stream.
		 * 
		 * @param stream the stream to send the plain output to
		 * @param indentSize the tab-size for indentation
		 */
		CodeStream(ostream& stream, unsigned short indentSize = 4);
		
		template <class T>
		CodeStream& operator<<(T data);
		CodeStream& operator<<(const char* data);
		CodeStream& operator<<(string data);
		CodeStream& operator<<(char c);
		CodeStream& put(char c);
		CodeStream& operator<< (ostream& ( *pf )(ostream&));
		CodeStream& operator<< (CodeStream& ( *pf )(CodeStream&));
		
		friend CodeStream& indent(CodeStream& stream);
		friend CodeStream& unindent(CodeStream& stream);
	
	private: // functions
		
		void maybe_indent();
	
	private: // variables
		
		ostream&             _stream;
		bool                 _newline;
		unsigned int         _indentation;
		const unsigned short _indentSize;
};

/******************************************************************************
 * Template Function Implementations                                          *
 ******************************************************************************/

template <class T>
CodeStream& CodeStream::operator<<(T data) {
	maybe_indent();
	_stream << data;
	return *this;
}

#endif // CODESTREAM_HPP
